/*
 * Copyright (c) 2017, salesforce.com, inc.
 * All rights reserved.
 * Licensed under the BSD 3-Clause license.
 * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
 */
// This is only done in tests because we are mocking things

import { Connection } from '@salesforce/core';
import { Source } from '@vscode/debugadapter';
import { DebugProtocol } from '@vscode/debugprotocol';
import {
  ApexDebug,
  ApexDebugStackFrameInfo,
  LaunchRequestArguments,
  VariableContainer
} from '../../../src/adapter/apexDebug';
import { Reference } from '../../../src/commands';
import { BreakpointService, SessionService, StreamingService } from '../../../src/core';
import { RequestService } from '../../../src/requestService/requestService';

export class ApexDebugForTest extends ApexDebug {
  private receivedResponses: DebugProtocol.Response[] = [];
  private receivedEvents: DebugProtocol.Event[] = [];

  constructor(
    requestService: RequestService,
    sessionService?: SessionService,
    streamingService?: StreamingService,
    breakpointService?: BreakpointService
  ) {
    super();
    this.myRequestService = requestService;
    this.mySessionService = sessionService ?? new SessionService(requestService);
    this.myStreamingService = streamingService ?? new StreamingService();
    this.myBreakpointService = breakpointService ?? new BreakpointService(requestService);
  }

  public getBreakpointService(): BreakpointService {
    return this.myBreakpointService;
  }

  public getRequestService(): RequestService {
    return this.myRequestService;
  }

  public getResponse(index: number): DebugProtocol.Response {
    return this.receivedResponses[index];
  }

  public getResponses(): DebugProtocol.Response[] {
    return this.receivedResponses;
  }

  public getEvents(): DebugProtocol.Event[] {
    return this.receivedEvents;
  }

  public sendResponse(response: DebugProtocol.Response): void {
    this.receivedResponses.push(response);
  }

  public sendEvent(event: DebugProtocol.Event): void {
    this.receivedEvents.push(event);
  }

  public initializeReq(
    response: DebugProtocol.InitializeResponse,
    args: DebugProtocol.InitializeRequestArguments
  ): void {
    super.initializeRequest(response, args);
  }

  public attachReq(response: DebugProtocol.AttachResponse, args: DebugProtocol.AttachRequestArguments): void {
    super.attachRequest(response, args);
  }

  public async launchRequest(response: DebugProtocol.LaunchResponse, args: LaunchRequestArguments): Promise<void> {
    return super.launchRequest(response, args);
  }

  public async getTargetOrgConnection(): Promise<Connection> {
    return super.getTargetOrgConnection();
  }

  public async disconnectReq(
    response: DebugProtocol.DisconnectResponse,
    args: DebugProtocol.DisconnectArguments
  ): Promise<void> {
    return super.disconnectRequest(response, args);
  }

  public async setBreakPointsReq(
    response: DebugProtocol.SetBreakpointsResponse,
    args: DebugProtocol.SetBreakpointsArguments
  ): Promise<void> {
    return super.setBreakPointsRequest(response, args);
  }

  public async continueReq(
    response: DebugProtocol.ContinueResponse,
    args: DebugProtocol.ContinueArguments
  ): Promise<void> {
    return super.continueRequest(response, args);
  }

  public async nextRequest(response: DebugProtocol.NextResponse, args: DebugProtocol.NextArguments): Promise<void> {
    await super.nextRequest(response, args);
  }

  public async stepInRequest(
    response: DebugProtocol.StepInResponse,
    args: DebugProtocol.StepInArguments
  ): Promise<void> {
    await super.stepInRequest(response, args);
  }

  public async stepOutRequest(
    response: DebugProtocol.StepOutResponse,
    args: DebugProtocol.StepOutArguments
  ): Promise<void> {
    await super.stepOutRequest(response, args);
  }

  public threadsReq(response: DebugProtocol.ThreadsResponse): void {
    super.threadsRequest(response);
  }

  public stackTraceRequest(
    response: DebugProtocol.StackTraceResponse,
    args: DebugProtocol.StackTraceArguments
  ): Promise<void> {
    return super.stackTraceRequest(response, args);
  }

  public async customRequest(command: string, response: DebugProtocol.Response, args: any): Promise<void> {
    return super.customRequest(command, response, args);
  }

  public setSalesforceProject(projectPath: string): void {
    this.salesforceProject = projectPath;
  }

  public addRequestThread(requestId: string): void {
    this.requestThreads.set(this.threadId++, requestId);
  }

  public getRequestThreads(): Map<number, string> {
    return this.requestThreads;
  }

  public printToDebugConsole(msg?: string, sourceFile?: Source, sourceLine?: number): void {
    super.printToDebugConsole(msg, sourceFile, sourceLine);
  }

  public errorToDebugConsole(msg?: string): void {
    super.errorToDebugConsole(msg);
  }

  public populateReferences(references: Reference[], requestId: string): void {
    super.populateReferences(references, requestId);
  }

  public getVariableContainer(variableReference: number): VariableContainer | undefined {
    return this.variableHandles.get(variableReference);
  }

  public createVariableContainer(variableContainer: VariableContainer): number {
    return this.variableHandles.create(variableContainer);
  }

  public getStackFrameInfo(frameId: number): ApexDebugStackFrameInfo {
    return this.stackFrameInfos.get(frameId);
  }

  public createStackFrameInfo(frameInfo: ApexDebugStackFrameInfo): number {
    return this.stackFrameInfos.create(frameInfo);
  }

  public getVariableContainerReferenceByApexId(): Map<number, number> {
    return this.variableContainerReferenceByApexId;
  }

  public async scopesRequest(
    response: DebugProtocol.ScopesResponse,
    args: DebugProtocol.ScopesArguments
  ): Promise<void> {
    return super.scopesRequest(response, args);
  }

  public async variablesRequest(
    response: DebugProtocol.VariablesResponse,
    args: DebugProtocol.VariablesArguments
  ): Promise<void> {
    return super.variablesRequest(response, args);
  }

  public getIdleTimers(): ReturnType<typeof setTimeout>[] {
    return this.idleTimers;
  }
}
